Enterprise Objects Framework Release 1.1 Copyright ã1995 by NeXT Computer, Inc.  All Rights Reserved.

EOKeyValueCoding
(informal protocol)

Category Of:	NSObject
Object

Declared In:	eoaccess/EOKeyValueCoding.h

Category Description

The methods of the EOKeyValueCoding category form the main transmission mechanism for data in the Enterprise Objects Framework, in which the properties of an enterprise object are accessed indirectly by name, rather than directly through invocation of an accessor method or as instance variables. Thus, any object can represent its state as an NSDictionary whose keys are the names of its properties.

The method for setting an enterprise object's values is takeValuesFromDictionary:, which uses the keys of the dictionary passed in to identify the new values for the object's properties. The method for getting values is valuesForKeys:, which extracts the value for each key in the array passed in and returns the key-value pairs in a dictionary. These two methods are defined for both NSObject and Object to use the accessor methods normally implemented by objects (or to access instance variables directly if need be), so that you don't have to write special code simply to integrate your enterprise objects into the Framework. Your enterprise objects can also override the default implementations to do custom processing of values as they enter and leave the objects.

Default Implementations

The Framework provides default implementations of takeValuesFromDictionary: and valuesForKeys: that work for all objects. The implementations for EOGenericRecord are quite simple: They simply store or retrieve the properties in a dictionary object held by the EOGenericRecord. NSDictionary and NSMutableDictionary, though not suitable for use as enterprise objects, meaningfully implement these methods by directly accessing their key-value pairs. The implementations for Object and NSObject, on the other hand, dynamically handle the keys requested based on the definition of the receiver's class. These implementations are general enough that your enterprise object classes should rarely need to override either key-value coding method.

In accessing an object's property, the default implementations of the key-value coding methods use the class definition as follows:

1.	The key-value coding method looks for an accessor method based on the property name. For example, with a property named lastName, takeValuesFromDictionary: looks for a method of the form setLastName: (note that the first letter of the property name is made uppercase), and valuesForKeys: looks for a method of the form lastName.

2.	If the key-value coding method doesn't find an accessor method, it looks for an instance variable whose name is the same as the property's and sets or retrieves its value directly. In setting an instance variable, takeValuesFromDictionary: retains the new value and releases the old one.

Type-checking and Type Conversion

The default implementations of the key-value coding methods accept any id type as a value, and do no type-checking or type conversion among object classes. It's possible, for example, to pass an NSString to takeValuesFromDictionary: as the value for a property the receiver expects to be an NSDate. The sender of a key-value coding message is thus responsible for ensuring that the values are of the proper class. An EOController, for example, converts string values from the user interface with its data source's coerceValue:forKey: method. If you're using a data source to manage your enterprise objects, you can use this method as well. Note that if you're using the Framework's access layer you can determine from an EOModel which value class is associated with each attributeÐEODatabaseDataSource uses this information in its implementation of coerceValue:forKey:.

The key-value coding methods handle one special case with regard to value types. Numeric values must be transferred in dictionaries as NSNumber objects, but aren't very efficient for calculation or storage; most objects store numeric properties as C scalar (numeric) types and declare their access methods to handle them in that form. In addition to being represented as NSNumbers in dictionaries, numeric property values are handled by an EOController as NSString objects. Because numeric properties are very common, the default implementations of the key-value coding methods convert an object value to the C scalar (numeric) type required by the enterprise object's accessor method or instance variable. For example, suppose your enterprise object defines these accessor methods:

- (void)setSalary:(unsigned int)salary
- (unsigned int)salary

For the setSalary: method, takeValuesFromDictionary: converts the object value for the ªsalaryº key in the dictionary to an unsigned int and passes it as salary. Similarly, valuesForKeys: converts the return value of the salary method to an NSNumber and inserts that into the dictionary that it returns.

The default implementations support the following scalar types:

char	unsigned char
short	unsigned short
int	unsigned int
long	unsigned long
float	double

Object values are converted to these types with the standard messages charValue, intValue, floatValue, and so on. Note that the key-value coding methods don't check that an object value actually responds to these messages; this can result in a run-time error if the object doesn't respond to the appropriate message.

An important issue to consider in using C scalar types is that most relational databases allow the use of a NULL value distinct from any numeric value, represented in the Enterprise Objects Framework by the EONull class. Since the C scalar types can't accommodate a distinct NULL value, the default implementations of the key-value coding methods raise NSInvalidArgumentException on encountering an EONull object that needs to be converted. You should either design your database not to use NULL values for numeric columns, or design your enterprise object class to use NSNumber objects where NULL values are allowed.

Implementing Key-Value Coding Methods

Your own enterprise object class can either rely on the default behavior of its superclass or override it, partially or completely. For example, a subclass of NSObject may have a few properties that it needs to handle in a special way, but the rest can all be handled by the default implementation. In this case, you might implement the methods of this category according to the following templates.

- (BOOL)takeValuesFromDictionary:(NSDictionary *)aDictionary
{
    NSMutableDictionary *mutDict;

    /* Make a mutable version of the passed-in dict. */
    mutDict = [[aDictionary mutableCopy] autorelease];

    /* Take whatever values you need from mutDictionary, removing them
     * from mutDict as you do so.
     */

    return [super takeValuesFromDictionary:mutDict];
}

takeValuesFromDictionary: here creates its own mutable copy of the dictionary passed in. It handles the keys it needs to, removing them from the dictionary so that the superclass' implementation won't set them as well, and then passes the remaining key-value pairs to super. To completely override the superclass' implementation this method would have to handle every key-value pair in the dictionary, returning YES if all key-value pairs were handled, and NO if any of them couldn't be processed.

- (NSDictionary *)valuesForKeys:(NSArray *)keys
{
    NSMutableArray *mutKeys = [[keys mutableCopy] autorelease];
    NSMutableDictionary *mutDict = [NSMutableDictionary dictionary];
    int i;

    for (i = [mutKeys count] - 1; i >= 0; i--) {

       /* See if you can put the value for the key at i into mutDict.
        * If your object stores the value as a mutable object you
        * should create an immutable copy and autorelease it before
        * putting it into mutDict. mutDict will ratain what you give
        * it as needed.
        */
        if (/* you put the value for the key at i in the dict */) {
            [mutKeys removeObjectAtIndex:i];
        }

    };

    [mutDict addEntriesFromDictionary:[super valuesForKeys:mutKeys]];

    return mutDict;
}

valuesForKeys: creates a mutable copy of the key array passed in, and a mutable dictionary in which to place the values requested. It handles the keys it needs to and removes them from the array, then passes the remaining keys to super and adds the returned values to the dictionary it's building. To completely override the superclass' implementation this method can simply omit the message to super after putting all the values it can find for the keys into the dictionary.

Cautions

Three situations demand care with regard to the key-value coding mechanism. First, be aware that the default implementation of takeValuesFromDictionary: doesn't guarantee the order that property values are set. Thus, your enterprise object's accessor methods shouldn't assume that other properties' values have been established when they're invoked.

Second, if your application is using the access layer, you should be careful in implementing accessor methods that set values (or in overriding takeValuesFromDictionary:) about sending messages to the value objects in the dictionary. takeValuesFromDictionary: is often sent while a fetch is in progress, and if your implementation sends a message to an EOFault object, it will try to fetch the object it stands in for, causing a server error. See the EODatabaseChannelNotification informal protocol specification for suggestions on how to work around this problem.

Third, it's possible for a property's value in a database to be NULL, in which case your enterprise object receives an EONull object as the value.  Similarly, if you delete the destination of a to-one relationship in a master-detail configuration, the corresponding property in the master is set to EONull. If either of these situations can occur, your enterprise objects must check whether the values they receive are EONulls, and treat them appropriately.

Method Types

Setting and getting values	- takeValuesFromDictionary:
- valuesForKeys:

Resetting key bindings	- flushKeyBindings

Instance Methods

flushKeyBindings

- (void)flushKeyBindings

Invalidates the cached key-binding information for the receiver's class. The Enterprise Objects Framework uses this information to optimize the default implementations of the other methods of this protocol by caching method selectors and instance variable type information.

This method should be invoked whenever a class is modified or removed from the runtime system.

takeValuesFromDictionary:

- (BOOL)takeValuesFromDictionary:(NSDictionary *)aDictionary

Sets properties of the receiver with values from aDictionary. Returns YES if the receiver read all values from the dictionary, NO if it couldn't take all values.

Note:  The access and interface layers of the Enterprise Objects Framework don't consider a return value of NO to be an error, and take no special action based on such a return value.

valuesForKeys:

- (NSDictionary *)valuesForKeys:(NSArray *)keyArray

Returns a dictionary providing as many values as possible for the keys in keyArray. Not all values requested are guaranteed to be retrieved. Note that it's possible for a property's value in a database to be NULL, in which case your enterprise object receives an EONull object as the value. Similarly, if you delete the destination of a to-one relationship in a master-detail configuration, the corresponding property in the master is set to EONull.